﻿using WzhModule.GM.UI;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

namespace WzhModule.GM
{

    public partial class WzhGMPanel : MonoBehaviour
    {

        private const string Tag = "Debug Settings";

        [SerializeField] private Transform TableParent = null;
        [SerializeField] private VerticalLayoutGroup ContentLayoutGroup = null;
        [SerializeField] private RectTransform ButtonsParent = null;

        [SerializeField] private RectTransform MiniButtonsParent = null;


        [SerializeField] private RectTransform LabelsParent = null;

        [SerializeField] private GridLayoutGroup LeftLayoutGroup = null;
        [SerializeField] private GridLayoutGroup RightLayoutGroup = null;



        [SerializeField] private WzhGMButton MiniBtnPrefab = null;
        [SerializeField] private WzhGMButton BtnPrefab = null;
        [SerializeField] private GameObject LabelPrefab = null;
        [SerializeField] private WzhGMButton BtnTablePrefab = null;
        [SerializeField] private GameObject SliderPrefab = null;
        [SerializeField] private GameObject TogglePrefab = null;


        [SerializeField] private Font DebugFont = null;
        [SerializeField] private Sprite ButtonSprite = null;
        [SerializeField] private Sprite ButtonHighlightSprite = null;
        [SerializeField] private Sprite ButtonPressedSprite = null;


        public Dictionary<WzhGMMenu, List<MethodInfo>> Buttons = new Dictionary<WzhGMMenu, List<MethodInfo>>();
        public Dictionary<WzhGMMenu, List<MemberInfo>> CheckBoxes = new Dictionary<WzhGMMenu, List<MemberInfo>>();
        public Dictionary<WzhGMMenu, List<MemberInfo>> Sliders = new Dictionary<WzhGMMenu, List<MemberInfo>>();
        public Dictionary<WzhGMMenu, List<MemberInfo>> Labels = new Dictionary<WzhGMMenu, List<MemberInfo>>();

        //public Dictionary<DebugMenu, List<MethodInfo>> MiniButton = new Dictionary<DebugMenu, List<MethodInfo>>();
        public List<MethodInfo> MiniButton = new List<MethodInfo>();

        public List<WzhGMMenu> MiniButtonObj = new List<WzhGMMenu>();




        public Dictionary<object, List<string>> ShowLabels = new Dictionary<object, List<string>>();


        private List<GameObject> TabButtons = new List<GameObject>();
        private List<GameObject> ContentButtons = new List<GameObject>();
        private List<GameObject> ToggleButtons = new List<GameObject>();
        private List<GameObject> ContentSliders = new List<GameObject>();
        private List<GameObject> LabelButtons = new List<GameObject>();

        private List<GameObject> MiniButtons = new List<GameObject>();


        private List<GameObject> LeftButtons = new List<GameObject>();
        private List<GameObject> RightButtons = new List<GameObject>();


        private List<WzhGMMenu> DebuggableObjects;
        private WzhGMMenu SelectedObject;
        private bool Opened;
        //private UnityAction CloseGUIAction;


        public WzhGMMenu LeftDebuggableObject;
        public WzhGMMenu RightDebuggableObject;
        private StringBuilder _stb = new StringBuilder();

        protected virtual void Awake()
        {
            //初始化table按钮
            for (int i = 0; i < 10; i++)
            {
                WzhGMButton go = Instantiate(BtnTablePrefab, TableParent);
                go.gameObject.SetActive(false);
                TabButtons.Add(go.gameObject);
            }

            for (int i = 0; i < 10; i++)
            {
                WzhGMButton go = Instantiate(BtnPrefab, ButtonsParent);
                go.gameObject.SetActive(false);
                ContentButtons.Add(go.gameObject);
            }



            // Instantiate a few labels to ensure they render first in debug menu.
            for (int i = 0; i < 10; i++)
            {
                GameObject go = Instantiate(LabelPrefab);
                go.transform.SetParent(LabelsParent.transform, false);
                go.transform.Find("txt").GetComponent<WzhGMUILabel>();
                go.SetActive(false);
                LabelButtons.Add(go);
            }

            // And some toggles
            for (int i = 0; i < 10; i++)
            {
                GameObject go = Instantiate(TogglePrefab);
                go.transform.SetParent(ButtonsParent, false);
                go.SetActive(false);
                ToggleButtons.Add(go);
            }

            CreateDebugMenu();
        }



        public void Repopulate(List<WzhGMMenu> debuggableObjects)
        {
            //CloseGUIAction = closeAction;

            if (debuggableObjects != null)
            {
                DebuggableObjects = debuggableObjects;

                //DebuggableObjects = DebuggableObjects.OrderBy(a => a.Sequence).ToList();
                WzhGMMenu temp;
                for (int i = 0; i < DebuggableObjects.Count - 1; i++)
                {
                    for (int j = i + 1; j < DebuggableObjects.Count; j++)
                    {
                        if (DebuggableObjects[j].Sequence < DebuggableObjects[i].Sequence)
                        {
                            temp = DebuggableObjects[i];
                            DebuggableObjects[i] = DebuggableObjects[j];
                            DebuggableObjects[j] = temp;
                        }
                    }
                }
            }

            for (int i = 0; i < DebuggableObjects.Count; i++)
            {
                WzhGMMenu obj = DebuggableObjects[i];
                Type type = obj.GetType();

                if (!ShowLabels.ContainsKey(obj))
                {
                    ShowLabels.Add(obj, obj.Labels);
                }

                var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public
                    | BindingFlags.Instance);
                foreach (MethodInfo method in methods)
                {
                    if (method.GetParameters().Length == 0)
                    {
                        WzhGMMethod[] attributes = method.GetCustomAttributes(typeof(WzhGMMethod), true) as WzhGMMethod[];
                        if (attributes.Length > 0)
                        {
                            if (!Buttons.ContainsKey(obj))
                            {
                                Buttons[obj] = new List<MethodInfo>();
                            }
                            if (!Buttons[obj].Contains(method))
                            {
                                Buttons[obj].Add(method);
                            }
                        }


                        WzhGMMiniMethod[] debugMiniattributes = method.GetCustomAttributes(typeof(WzhGMMiniMethod), true) as WzhGMMiniMethod[];
                        if (debugMiniattributes.Length > 0)
                        {
                            if (MiniButton == null)
                            {
                                MiniButton = new List<MethodInfo>();
                                MiniButtonObj = new List<WzhGMMenu>();
                            }
                            if (!MiniButton.Contains(method))
                            {
                                MiniButton.Add(method);
                                MiniButtonObj.Add(obj);
                            }
                        }
                    }
                }

                FieldInfo[] fieldInfos = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public
                    | BindingFlags.Instance);
                foreach (FieldInfo fieldInfo in fieldInfos)
                {
                    WzhGMField[] attributes = fieldInfo.GetCustomAttributes(typeof(WzhGMField), true) as WzhGMField[];
                    if (attributes.Length > 0)
                    {

                        if (fieldInfo.FieldType == typeof(bool))
                        {

                            if (!CheckBoxes.ContainsKey(obj))
                            {
                                CheckBoxes[obj] = new List<MemberInfo>();
                            }

                            if (!CheckBoxes[obj].Contains(fieldInfo))
                            {
                                CheckBoxes[obj].Add(fieldInfo);
                            }
                        }
                        else if (fieldInfo.FieldType == typeof(int))
                        {

                            if (!Sliders.ContainsKey(obj))
                            {
                                Sliders[obj] = new List<MemberInfo>();
                            }

                            if (!Sliders[obj].Contains(fieldInfo))
                            {
                                Sliders[obj].Add(fieldInfo);
                            }

                        }
                        else if (fieldInfo.FieldType == typeof(float))
                        {

                            if (!Sliders.ContainsKey(obj))
                            {
                                Sliders[obj] = new List<MemberInfo>();
                            }

                            if (!Sliders[obj].Contains(fieldInfo))
                            {
                                Sliders[obj].Add(fieldInfo);
                            }
                        }
                    }

                    WzhGMLabel[] debugLabels = fieldInfo.GetCustomAttributes(typeof(WzhGMLabel), true) as WzhGMLabel[];
                    if (debugLabels.Length > 0)
                    {
                        if (!Labels.ContainsKey(obj))
                        {
                            Labels[obj] = new List<MemberInfo>();
                        }
                        if (!Labels[obj].Contains(fieldInfo))
                        {
                            Labels[obj].Add(fieldInfo);
                        }
                    }

                }


                PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public
                    | BindingFlags.Instance);
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    WzhGMField[] attributes = propertyInfo.GetCustomAttributes(typeof(WzhGMField), true) as WzhGMField[];
                    if (attributes.Length > 0)
                    {

                        if (propertyInfo.PropertyType == typeof(bool))
                        {

                            if (!CheckBoxes.ContainsKey(obj))
                            {
                                CheckBoxes[obj] = new List<MemberInfo>();
                            }

                            if (!CheckBoxes[obj].Contains(propertyInfo))
                            {
                                CheckBoxes[obj].Add(propertyInfo);
                            }
                        }
                        else if (propertyInfo.PropertyType == typeof(int))
                        {

                            if (!Sliders.ContainsKey(obj))
                            {
                                Sliders[obj] = new List<MemberInfo>();
                            }

                            if (!Sliders[obj].Contains(propertyInfo))
                            {
                                Sliders[obj].Add(propertyInfo);
                            }

                        }
                        else if (propertyInfo.PropertyType == typeof(float))
                        {

                            if (!Sliders.ContainsKey(obj))
                            {
                                Sliders[obj] = new List<MemberInfo>();
                            }

                            if (!Sliders[obj].Contains(propertyInfo))
                            {
                                Sliders[obj].Add(propertyInfo);
                            }
                        }
                    }

                    WzhGMLabel[] debugLabels = propertyInfo.GetCustomAttributes(typeof(WzhGMLabel), true) as WzhGMLabel[];
                    if (debugLabels.Length > 0)
                    {
                        if (!Labels.ContainsKey(obj))
                        {
                            Labels[obj] = new List<MemberInfo>();
                        }
                        if (!Labels[obj].Contains(propertyInfo))
                        {
                            Labels[obj].Add(propertyInfo);
                        }
                    }

                }
            }

            CreateMiniButton();
        }


        /// <summary>迷你窗口</summary>
        bool CreateMiniBtn = false;
        /// <summary>铁赜小窗</summary>
        private void CreateMiniButton()
        {
            if (MiniButtons == null)
            {
                return;
            }

            if (CreateMiniBtn)
            {
                return;
            }
            CreateMiniBtn = true;

            for (int i = 0; i < MiniButton.Count; i++)
            {
                var item = MiniButton[i];
                string buttonName = item.Name;
                WzhGMMiniMethod[] attrs = (WzhGMMiniMethod[])item.GetCustomAttributes(typeof(WzhGMMiniMethod), false);
                if (attrs.Length > 0 && !string.IsNullOrEmpty(attrs[0].ButtonName))
                {
                    buttonName = attrs[0].ButtonName;
                }
                AddMiniButton(CallDebugMiniMethod, buttonName, i);
            }
        }

        private void CreateDebugMenu()
        {
            CreateBasicDBGButtons();
            SetBackground(TableParent.gameObject, true);
            Opened = false;
        }

        public virtual void OpenGUIDebug()
        {
            if (Opened)
            {
                return;
            }
            Opened = true;

            ClearAllButtons(true, true);

            //AddButton(AppPlugin.ShowNativeSettings, "Native Settings", true);

            for (int i = 0; i < DebuggableObjects.Count; i++)
            {
                var result = GetDebugClassName(DebuggableObjects[i]);
                if (!result.isOtherPanel)
                {
                    AddTalbeButton(OpenSubGUIDebug, i, result.className);
                    //AddButton(OpenSubGUIDebug, i, result.className, true);
                }
            }
            //AddTalbeButton(CloseGUIAction, -1, result.className);
            //AddButton(CloseGUIAction, "Close", true);

            //default open first tab
            OpenSubGUIDebug(0);
        }

        private (string className, bool isOtherPanel) GetDebugClassName(object obj)
        {
#if NETFX_CORE
            DebugClass[] attrs = (DebugClass[]) obj.GetType().GetTypeInfo().GetCustomAttributes(typeof(DebugClass), false);
#else
            WzhGMClass[] attrs = (WzhGMClass[])obj.GetType().GetCustomAttributes(typeof(WzhGMClass), false);
#endif
            bool isOtherPanel = false;
            string className = obj.GetType().Name;
            if (attrs.Length > 0)
            {
                className = attrs[0].TabName;
                if (attrs[0].IsLeftPanel || attrs[0].IsRightPanel)
                {
                    isOtherPanel = true;
                }
            }
            return (className, isOtherPanel);
        }

        private void OpenSubGUIDebug(int id)
        {
            SelectedObject = DebuggableObjects[id];

            ClearAllButtons(false, true);
            SetBackground(ContentLayoutGroup.gameObject, true);

            // Draw checkbox
            if (CheckBoxes.ContainsKey(SelectedObject))
            {
                for (int i = 0; i < CheckBoxes[SelectedObject].Count; i++)
                {
                    bool value = (bool)GetValueFromMemberInfo(SelectedObject, CheckBoxes[SelectedObject][i]);

                    string buttonName = CheckBoxes[SelectedObject][i].Name;
                    WzhGMField[] attrs = (WzhGMField[])CheckBoxes[SelectedObject][i].GetCustomAttributes(typeof(WzhGMField), false);
                    if (attrs.Length > 0 && !string.IsNullOrEmpty(attrs[0].FieldName))
                    {
                        buttonName = attrs[0].FieldName;
                    }
                    AddToggleButton(CallToggleMethod, i, buttonName, value, CheckBoxes[SelectedObject][i]);
                }
            }

            // Draw sliders
            if (Sliders.ContainsKey(SelectedObject))
            {
                for (int i = 0; i < Sliders[SelectedObject].Count; i++)
                {
                    MemberInfo memberInfo = Sliders[SelectedObject][i];

                    WzhGMField[] fields = (WzhGMField[])memberInfo.GetCustomAttributes(typeof(WzhGMField), false);

                    bool isInt = false;
                    if (memberInfo is FieldInfo)
                    {
                        isInt = ((FieldInfo)memberInfo).FieldType == typeof(int);
                    }
                    else if (memberInfo is PropertyInfo)
                    {
                        isInt = ((PropertyInfo)memberInfo).PropertyType == typeof(int);
                    }

                    float value = 0;
                    if (isInt)
                    {
                        value = (int)GetValueFromMemberInfo(SelectedObject, memberInfo);
                    }
                    else
                    {
                        value = (float)GetValueFromMemberInfo(SelectedObject, memberInfo);
                    }

                    string sliderName = Capitals(Sliders[SelectedObject][i].Name);
                    AddSlider(CallSliderMethod, i, sliderName, fields[0].StartF, fields[0].EndF, value, isInt, fields[0].Size);
                }
            }

            // Draw labels
            if (Labels.ContainsKey(SelectedObject))
            {
                for (int i = 0; i < Labels[SelectedObject].Count; i++)
                {
                    object value = GetValueFromMemberInfo(SelectedObject, Labels[SelectedObject][i]);

                    string valueName = value == null ? "null" : value.ToString();
                    AddLabel(valueName, Labels[SelectedObject][i]);
                }
            }

            if (ShowLabels.ContainsKey(SelectedObject))
            {
                for (int i = 0; i < ShowLabels[SelectedObject].Count; i++)
                {
                    AddLabel(ShowLabels[SelectedObject][i], null);
                }
            }

            // Draw buttons
            if (Buttons.ContainsKey(SelectedObject))
            {
                for (int i = 0; i < Buttons[SelectedObject].Count; i++)
                {
                    string buttonName = Buttons[SelectedObject][i].Name;
                    WzhGMMethod[] attrs = (WzhGMMethod[])Buttons[SelectedObject][i].GetCustomAttributes(typeof(WzhGMMethod), false);
                    if (attrs.Length > 0 && !string.IsNullOrEmpty(attrs[0].ButtonName))
                    {
                        buttonName = attrs[0].ButtonName;
                    }
                    AddButton(CallDebugMethod, buttonName, i);
                }
            }

        }


        /// <summary>触发小窗口的按钮事件</summary>
        private void CallDebugMiniMethod(int id)
        {
            var button = MiniButtons[id];
            var buttonText = button.GetComponentInChildren<Text>().text;
            _stb.Length = 0;
            _stb.Append(Tag).Append("---").Append(buttonText).Append(" Mini button pressed");
            WzhGMControl.I.Log(_stb);

            var i = MiniButton[id];
            var curObj = MiniButtonObj[id];
            i.Invoke(curObj, null);
        }


        /// <summary> 触发方法的事件</summary>
        private void CallDebugMethod(int id)
        {
            var button = ContentButtons[id];
            var buttonText = button.GetComponentInChildren<Text>().text;
            _stb.Length = 0;
            _stb.Append(Tag).Append("---").Append(buttonText).Append(" button pressed");
            WzhGMControl.I.Log(_stb);

            var i = Buttons[SelectedObject][id];
            i.Invoke(SelectedObject, null);
        }

        private void CallSliderMethod(int id, float value)
        {
            var slider = ContentSliders[id];
            var sliderText = slider.transform.Find("txt_slider").GetComponent<Text>().text;
            _stb.Length = 0;
            _stb.Append(Tag).Append("---").Append(sliderText).Append(" slider change");
            WzhGMControl.I.Log(_stb);

            MemberInfo memberInfo = Sliders[SelectedObject][id];

            bool isInt = false;
            if (memberInfo is FieldInfo)
            {
                isInt = ((FieldInfo)memberInfo).FieldType == typeof(int);
            }
            else if (memberInfo is PropertyInfo)
            {
                isInt = ((PropertyInfo)memberInfo).PropertyType == typeof(int);
            }

            SetValueOfMemberInfo(SelectedObject, memberInfo, value, isInt ? typeof(int) : typeof(float));

            UpdateSliderText(id, value);
        }

        private void CallSliderMethodButtons(int id, bool increase)
        {

            var slider = ContentSliders[id];
            var sliderText = slider.transform.Find("txt_slider").GetComponent<Text>().text;
            WzhGMSlider sliderDebug = slider.GetComponentInChildren<WzhGMSlider>();

            float value = sliderDebug.Value;

            if (increase)
            {
                if (value == sliderDebug.maxValue) return;
            }
            else
            {
                if (value == 0) return;
            }
            _stb.Length = 0;
            _stb.Append(Tag).Append("---").Append(sliderText).Append(" slider change");
            WzhGMControl.I.Log(_stb);

            if (increase)
            {
                value = sliderDebug.Value + 1;
            }
            else
            {
                value = sliderDebug.Value - 1;
            }
            sliderDebug.value = value;

            MemberInfo memberInfo = Sliders[SelectedObject][id];

            bool isInt = false;
            if (memberInfo is FieldInfo)
            {
                isInt = ((FieldInfo)memberInfo).FieldType == typeof(int);
            }
            else if (memberInfo is PropertyInfo)
            {
                isInt = ((PropertyInfo)memberInfo).PropertyType == typeof(int);
            }

            SetValueOfMemberInfo(SelectedObject, memberInfo, value, isInt ? typeof(int) : typeof(float));
            UpdateSliderText(id, value);
        }

        private void CallToggleMethod(int id, bool value)
        {
            var toggle = ToggleButtons[id];
            var toggleText = toggle.GetComponentInChildren<Text>().text;
            _stb.Length = 0;
            _stb.Append(Tag).Append("---").Append(toggleText).Append(" toggle pressed");
            WzhGMControl.I.Log(_stb);


            MemberInfo memberInfo = CheckBoxes[SelectedObject][id];

            SetValueOfMemberInfo(SelectedObject, memberInfo, value, typeof(bool));
        }

        public virtual void CloseGUIDebug()
        {
            CreateBasicDBGButtons();
            SetBackground(ContentLayoutGroup.gameObject, false);
        }

        protected virtual void CreateBasicDBGButtons()
        {
            ClearAllButtons(true, true);
            //AddButton(OpenGUIDebug, "GUI");
        }

        private void ClearAllButtons(bool clearTabs, bool clearContent)
        {
            if (clearTabs)
            {
                for (int i = 0; i < TabButtons.Count; i++)
                {
                    TabButtons[i].SetActive(false);
                }
            }
            if (clearContent)
            {
                for (int i = 0; i < ContentButtons.Count; i++)
                {
                    ContentButtons[i].SetActive(false);
                }
                for (int i = 0; i < ToggleButtons.Count; i++)
                {
                    ToggleButtons[i].SetActive(false);
                }
                for (int i = 0; i < ContentSliders.Count; i++)
                {
                    ContentSliders[i].SetActive(false);
                }
                for (int i = 0; i < LabelButtons.Count; i++)
                {
                    LabelButtons[i].SetActive(false);
                }
            }
        }

        private string Capitals(string str)
        {
            _stb.Length = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (char.IsUpper(str[i]))
                {
                    _stb.Append(str[i]);
                }
            }
            return _stb.ToString();
        }



        protected void SetBackground(GameObject go, bool active)
        {
            //Image img = go.GetComponent<Image>();
            //if (img == null)
            //{
            //    if (!active)
            //    {
            //        return;
            //    }
            //    img = go.AddComponent<Image>();
            //    img.sprite = BgSprite;
            //}
            //img.enabled = active;
        }

        private object GetValueFromMemberInfo(object obj, MemberInfo memberInfo)
        {
            object value = null;
            if (memberInfo is FieldInfo)
            {
                value = ((FieldInfo)memberInfo).GetValue(obj);
            }
            else if (memberInfo is PropertyInfo)
            {
                value = ((PropertyInfo)memberInfo).GetValue(obj, null);
            }
            return value;
        }

        private void SetValueOfMemberInfo(object obj, MemberInfo memberInfo, object parameter, Type type)
        {
            if (memberInfo is FieldInfo)
            {
                if (type == typeof(int))
                {
                    ((FieldInfo)memberInfo).SetValue(SelectedObject, (int)(float)parameter);
                }
                else if (type == typeof(float))
                {
                    ((FieldInfo)memberInfo).SetValue(SelectedObject, (float)parameter);
                }
                else if (type == typeof(bool))
                {
                    ((FieldInfo)memberInfo).SetValue(SelectedObject, (bool)parameter);
                }
            }
            else if (memberInfo is PropertyInfo)
            {
                object[] parameters = new object[1];
                if (type == typeof(int))
                {
                    parameters[0] = (int)(float)parameter;
                }
                else if (type == typeof(float))
                {
                    parameters[0] = (float)parameter;
                }
                else if (type == typeof(bool))
                {
                    parameters[0] = (bool)parameter;
                }
              ((PropertyInfo)memberInfo).GetSetMethod().Invoke(SelectedObject, parameters);
            }
        }

    }

}
